package agent

import (
	"context"
	"database/sql"
	"fmt"
	"net"

	"gitee.com/xuender/mysql-proxy/pb"
	"github.com/xelabs/go-mysqlstack/sqlparser/depends/query"
	"github.com/xuender/oils/base"
	"github.com/xuender/oils/logs"
	"github.com/xuender/oils/oss"
)

type Service struct {
	c  *pb.Config
	db *sql.DB
}

func NewService(config *pb.Config) *Service {
	dsn := "hexin:hexin@tcp(192.168.1.10:3306)/test?charset=utf8mb4&parseTime=True"

	return &Service{
		c:  config,
		db: base.Panic1(sql.Open("mysql", dsn)),
	}
}

func (p *Service) query(msg *pb.Msg) {
	rows := base.Panic1(p.db.Query(msg.Sql))
	defer rows.Close()

	types := base.Panic1(rows.ColumnTypes())

	if len(types) == 0 {
		return
	}

	msg.Fields = make([]*pb.Field, len(types))

	for index, fieldType := range types {
		field := &pb.Field{Name: fieldType.Name()}

		if num, has := query.Type_value[fieldType.DatabaseTypeName()]; has {
			field.Type = num
		} else {
			switch fieldType.DatabaseTypeName() {
			case "INT":
				field.Type = int32(query.Type_INT32)
			case "BIGINT":
				field.Type = int32(query.Type_INT64)
			default:
				logs.Warnw("Type", "DatabaseTypeName", fieldType.DatabaseTypeName())
			}
		}

		msg.Fields[index] = field
	}

	msg.Rows = []*pb.Row{}

	for rows.Next() {
		values := make([][]byte, len(msg.Fields))
		data := make([]any, len(msg.Fields))

		for i := range msg.Fields {
			data[i] = &values[i]
		}

		_ = rows.Scan(data...)

		row := &pb.Row{}
		row.Data = make([][]byte, len(msg.Fields))

		for i := range msg.Fields {
			row.Data[i] = values[i]
		}

		msg.Rows = append(msg.Rows, row)
	}

	logs.Infow("query", "msg", msg)
}

func (p *Service) Call(ctx context.Context, msg *pb.Msg) (ret *pb.Msg, err error) {
	ret = msg

	defer p.Recover(ret)

	if msg.IsQuery() {
		p.query(ret)

		return
	}

	r := base.Panic1(p.db.Exec(msg.Sql))
	ret.InsertID = uint64(base.Panic1(r.LastInsertId()))
	ret.RowsAffected = uint64(base.Panic1(r.RowsAffected()))

	return
}

func (p *Service) Recover(msg *pb.Msg) {
	if rec := recover(); rec != nil {
		switch err := rec.(type) {
		case error:
			msg.Error = err.Error()
		case string:
			msg.Error = err
		default:
			msg.Error = fmt.Sprintf("%v", err)
		}

		logs.Error(msg.Error)
	}
}

func (p *Service) Run() {
	lis := base.Panic1(net.Listen("tcp", fmt.Sprintf(":%d", p.c.GrpcPort)))
	// server := grpc.NewServer()

	// pb.RegisterCallerServer(server, p)
	oss.SignalClose(p, lis)
	// _ = server.Serve(lis)
	logs.Infow("run", "port", p.c.GrpcPort)
}

func (p *Service) Error(err error) {
	logs.Error(err)
}
